﻿using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Assertion.NUnit
{
    [TestFixture, Category("Assertion")]
    public class TestAssertion
    {
        class FakeInteger : IDisposable
        {
            public int i;
            public void Dispose() { i = 0; }
            public static implicit operator int(FakeInteger value) { return value.i; }
        }

        [Test]
        public void FalseResult()
        {
            Assertion test = new Assertion();
            test.Add(() => (true));
            test.Add(() => (false));
            Assert.IsFalse(test);
            Assert.That(test.AllChecks().Count() == 2);
            Assert.That(test.FailedChecks().Count() == 1);
            Assert.That(test.SuccessfulChecks().Count() == 1);
        }

        [Test]
        public void TrueResult()
        {
            int x = 2;

            Assertion test = new Assertion();
            test.Add(() => (true));
            test.Add(() => (x * 2 == 4));
            Assert.IsTrue(test);
            Assert.That(test.AllChecks().Count() == 2);
            Assert.That(test.FailedChecks().Count() == 0);
            Assert.That(test.SuccessfulChecks().Count() == 2);

            x = 4;
            test.ReCheck();
            Assert.IsFalse(test);
            Assert.That(test.FailedChecks().Count() == 1);
            Assert.That(test.SuccessfulChecks().Count() == 1);
        }

        [Test]
        public void AssertionComparisionByContent()
        {
            int x = 2;
            int y = 4;

            Assertion test1 = new Assertion();
            test1.Add(() => (y / 2 == 2));
            test1.Add(() => (x * 2 == 4));

            Assertion test2 = new Assertion();
            test2.Add(() => (x * 2 == 4));
            test2.Add(() => (y / 2 == 2));

            Assert.That(test1.IsIdenticalByContent(test2));
            Assert.That(test1.IsIdenticalByContentAndResults(test2));
        }

        [Test]
        public void ResultSavingAfterDispose()
        {
            Assertion test1 = new Assertion();
            Assertion test2 = new Assertion();

            using (FakeInteger y = new FakeInteger() { i = 4 })
            {
                test1.Add(() => (y / 2 == 2));
            }
            Assert.IsFalse(test1);

            using (FakeInteger y = new FakeInteger() { i = 4 })
            {
                test2.Add(() => (y / 2 == 2));
                test2.ReCheck();
            }
            Assert.IsTrue(test2);

        }

        [Test]
        public void AssertionComparisionByResult()
        {
            int x = 2;
            Assertion test1 = new Assertion();
            Assertion test2 = new Assertion();
            Assertion test3 = new Assertion();
            Assertion test4 = new Assertion();

            using (FakeInteger y = new FakeInteger() { i = 4 })
            {
                test1.Add(() => (x * 2 == 4));
                test1.Add(() => (y / 2 == 2));
                test1.ReCheck();
            }

            using (FakeInteger y = new FakeInteger() { i = 4 })
            {
                test2.Add(() => (y / 2 == 2));
                test2.Add(() => (x * 2 == 4));
                test2.ReCheck();
            }

            using (FakeInteger y = new FakeInteger() { i = 3 })
            {
                test3.Add(() => (y / 2 == 2));
                test3.Add(() => (x * 2 == 4));
                test3.ReCheck();
            }

            using (FakeInteger y = new FakeInteger())
            {
                int z = 2012;
                y.i = z - 2008;

                test4.Add(() => (y / 2 == 2));
                test4.Add(() => (x * 2 == 4));
                test4.ReCheck();
            }

            Assert.IsTrue(test1);

            Assert.IsTrue(test2);
            Assert.IsTrue(test1.IsIdenticalByContent(test2));
            Assert.IsTrue(test1.IsIdenticalByContentAndResults(test2));

            Assert.IsFalse(test3);
            Assert.IsTrue(test1.IsIdenticalByContent(test3));
            Assert.IsFalse(test1.IsIdenticalByContentAndResults(test3));

            Assert.IsTrue(test4);
            Assert.IsTrue(test1.IsIdenticalByContent(test4));
            Assert.IsTrue(test1.IsIdenticalByContentAndResults(test4));
        }

        [Test]
        public void TestOutput()
        {
            int x = 2, r1 = 4;
            int y = 4, r2 = 2;

            Assertion test1 = new Assertion(IsPrintableOnConvertToBool: true);
            test1.Add(() => (x * 2 == r1));
            test1.Add(() => (y / 2 == r2));

            Assert.That(test1.ToStringShort() == "No failed checks in Assertion");

            Assertion test2 = new Assertion();
            test2.Add(() => (x * 2 == 5));

            Console.WriteLine(test2.ToStringShort());

            Assert.That(test2.ToStringShort().Contains("Failed  ((x * 2) == 5)"));
        }

        [Test]
        public void AssertionDiffByContent()
        {
            int x = 4, y = 8, z = 6;

            Assertion test1 = new Assertion();
            test1.Add(() => (x * 2 == 4));
            test1.Add(() => (y / 2 == 4));

            Assertion test2 = new Assertion();
            test2.Add(() => (x * 2 == 4));
            test2.Add(() => (z + 2 == 8));

            List<Assertion.Check> diff1 = test1.DiffByContentLeftOnly(test2);
            Assert.That(diff1.Count() == 1);
            Assert.That(diff1.FirstOrDefault().Name == "((y / 2) == 4)");

            List<Assertion.Check> diff2 = test1.DiffByContentRightOnly(test2);
            Assert.That(diff2.Count() == 1);
            Assert.That(diff2.FirstOrDefault().Name == "((z + 2) == 8)");
        }

        [Test]
        public void AssertionDiffByContentAndResult()
        {
            int x = 2, z = 8;
            Assertion test1 = new Assertion();
            Assertion test2 = new Assertion();

            using (FakeInteger y = new FakeInteger() { i = 8 })
            {
                test1.Add(() => (y / 2 == 2));
                test1.Add(() => (x * 2 == 4));
                test1.Add(() => (z / 2 == 4));
                test1.ReCheck();
            }

            using (FakeInteger y = new FakeInteger() { i = 4 })
            {
                test2.Add(() => (y / 2 == 2));
                test2.Add(() => (x * 2 == 4));
                test2.Add(() => (z * 2 == 16));
                test2.ReCheck();
            }

            List<Assertion.Check> diffResult = test1.DiffByContentAndResultsLeftOnly(test2);
            Assert.That(diffResult.Count() == 2);
            Assert.That(diffResult.Exists(a => a.Name == "((Convert(y) / 2) == 2)" && a.IsFailed));
            Assert.That(diffResult.Exists(a => a.Name == "((z / 2) == 4)" && a.IsSuccessful));

            List<Assertion.Check> diffResult2 = test1.DiffByContentAndResultsRightOnly(test2);
            Assert.That(diffResult2.Count() == 2);
            Assert.That(diffResult2.Exists(a => a.Name == "((Convert(y) / 2) == 2)" && a.IsSuccessful));
            Assert.That(diffResult2.Exists(a => a.Name == "((z * 2) == 16)" && a.IsSuccessful));
        }
    }
}
